/**
 * 通过JSON进行深拷贝;
 * 1. undefined：会被丢弃.
 * 2. function：会被丢弃；
 * 3. Symbol：会被丢弃；
 * 4. Date：会被转换为字符串；
 * 5. RegExp：会被转换为空对象；
 * 6. Map|Set：会被转换为空对象；
 * 7. BigInt：会抛出 TypeError，因为 JSON 不支持 BigInt 类型；
 * 8. 传入的克隆对象中包含循环、递归引用,会抛出 TypeError；
 * 9. 传入的克隆对象中包含不可枚举的属性；
 * @param value 需要克隆的对象
 * @returns
 */
export function deepCloneByJSON(value: any) {
  return JSON.parse(JSON.stringify(value))
}

/**
 * 深度克隆
 * @param value 需要克隆的对象
 * @param hash
 * @returns
 */
export function deepClone<T>(value: T, hash = new WeakMap()): T {
  // 如果传入的值是null或者不是对象，则直接返回该值
  if (value === null || typeof value !== 'object') {
    return value
  }
  // 如果传入的值已经被克隆过，则直接返回克隆后的值
  if (hash.has(value)) {
    return hash.get(value)
  }
  // 如果传入的值是Date类型，则返回一个新的Date对象
  if (value instanceof Date) {
    return new Date(value.getTime()) as T
  }
  // 如果传入的值是RegExp类型，则返回一个新的RegExp对象
  if (value instanceof RegExp) {
    return new RegExp(value) as T
  }
  // 如果传入的值是Map类型，则返回一个新的Map对象
  if (value instanceof Map) {
    const map = new Map()
    hash.set(value, map)
    value.forEach((v, k) => {
      map.set(k, deepClone(v, hash))
    })
    return map as T
  }
  // 如果传入的值是Set类型，则返回一个新的Set对象
  if (value instanceof Set) {
    const set = new Set()
    hash.set(value, set)
    value.forEach((v) => {
      set.add(deepClone(v, hash))
    })
    return set as T
  }
  // 如果传入的值是ArrayBuffer类型，则返回一个新的ArrayBuffer对象
  if (value instanceof ArrayBuffer) {
    return value.slice(0) as T
  }
  // 如果传入的值是ArrayBuffer的视图类型，则返回一个新的视图对象
  if (ArrayBuffer.isView(value)) {
    return new (value.constructor as any)(value) as T
  }
  // 如果传入的值是Symbol类型，则返回一个新的Symbol对象
  if (typeof value === 'symbol') {
    return Object(Symbol.prototype.valueOf.call(value)) as T
  }
  // 如果传入的值是数组，则返回一个新的空数组，否则返回一个新的对象
  const objCopy = Array.isArray(value)
    ? []
    : Object.create(Object.getPrototypeOf(value))
  // 将传入的值和克隆后的值放入hash中
  hash.set(value, objCopy)
  // 遍历传入的值的属性，将属性值进行深度克隆，并赋值给克隆后的对象
  Reflect.ownKeys(value).forEach((key) => {
    ;(objCopy as any)[key] = deepClone((value as any)[key], hash)
  })
  // 返回克隆后的对象
  return objCopy as T
}

/**
 * 利用原生的结构化克隆算法进行深拷贝
 * 1. function：抛出 DATA_CLONE_ERR 的异常；
 * 2. DOM 节点同样会抛出 DATA_CLONE_ERR 异常；
 * 3. RegExp 对象的 lastIndex 字段不会被保留；
 * 4. 属性描述符，setters 以及 getters（以及其他类似元数据的功能）同样不会被复制。
 * 5. 原形链上的属性也不会被追踪以及复制。
 * @param value 需要克隆的对象
 * @returns
 */
export const structClone = (value: any) => structuredClone(value)

interface IObj {
  [key: string]: any
}

type IProps = keyof IObj

/**
 * 挑选对象属性组成一个新对象
 * @param obj 需要筛选的对象
 * @param props 要挑选的对象属性
 */
export const pick = (obj: IObj, ...props: IProps[]) => {
  Object.fromEntries(Object.entries(obj).filter(([key]) => props.includes(key)))
}
